﻿using System.Runtime.CompilerServices;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;

public static class Helper
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static (int, int) IndexToXY(int index, int width)
    {
        int x = index % width;
        int y = (index - x) / width;
        return (x, y);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int XYToIndex(int x, int y, int width)
    {
        int index = x + y * width;
        return index;
    }
}

/// <summary>
/// 读取SourceTexture的Job.
/// Burst编译器能加速10倍以上.
/// </summary>
[BurstCompile]
public struct TextureReadJob : IJobParallelFor
{
    [ReadOnly]
    public NativeArray<Color32> SourceColors;

    public NativeArray<bool> SourceIsIn;

    public void Execute(int index)
    {
        Color color = SourceColors[index];
        SourceIsIn[index] = color == Color.white ? true : false;
    }
}

/// <summary>
/// 写入DestinnationTexture的Job
/// </summary>
[BurstCompile]
public struct SdfCaculateJob : IJobParallelFor
{
    [ReadOnly] public int sourceWidth;
    [ReadOnly] public int sourceHeight;

    [ReadOnly] public int TexWidth;
    [ReadOnly] public int TexHeight;

    [ReadOnly] public int MaxSearchDistance;
    [ReadOnly] public NativeArray<bool> SourceIsIn;

    public NativeArray<float> TargetDistance;
    public NativeArray<float> MinDist;
    public NativeArray<float> MaxDist;

    public int gapX, gapY;

    /// <summary>
    /// 调试产生
    /// </summary>
    /// <param name="index"></param>
    public void _Execute(int index)
    {
        byte value = (byte)(SourceIsIn[index] ? 127 : 0);
    }

    public void Execute(int index)
    {
        int minx, maxx, miny, maxy;
        float max_distance = -MaxSearchDistance;
        float min_distance = MaxSearchDistance;

        //int y = index % TexWidth;
        //int x = (index - y) / TexWidth;

        var (x, y) = Helper.IndexToXY(index, TexWidth);

        int sourceX = x * gapX;
        int sourceY = y * gapY;
        int min = MaxSearchDistance;
        minx = sourceX - MaxSearchDistance;
        if (minx < 0)
        {
            minx = 0;
        }
        miny = sourceY - MaxSearchDistance;
        if (miny < 0)
        {
            miny = 0;
        }
        maxx = sourceX + MaxSearchDistance;
        if (maxx > (int)sourceWidth)
        {
            maxx = sourceWidth;
        }
        maxy = sourceY + MaxSearchDistance;
        if (maxy > (int)sourceHeight)
        {
            maxy = sourceHeight;
        }
        int dx, dy, iy, ix, distance;
        bool sourceIsInside = SourceIsIn[Helper.XYToIndex(sourceX, sourceY, sourceWidth)];// SourceIsIn[sourceY * sourceWidth + sourceX];
        if (sourceIsInside)
        {
            for (iy = miny; iy < maxy; iy++)
            {
                dy = iy - sourceY;
                dy *= dy;
                for (ix = minx; ix < maxx; ix++)
                {
                    bool targetIsInside = SourceIsIn[Helper.XYToIndex(ix, iy, sourceWidth)];//SourceIsIn[iy * sourceWidth + ix];
                    if (targetIsInside)
                    {
                        continue;
                    }
                    dx = ix - sourceX;
                    distance = (int)Mathf.Sqrt(dx * dx + dy);
                    if (distance < min)
                    {
                        min = distance;
                    }
                }
            }

            if (min > max_distance)
            {
                max_distance = min;
            }

            TargetDistance[index] = min;
        }
        else
        {
            for (iy = miny; iy < maxy; iy++)
            {
                dy = iy - sourceY;
                dy *= dy;
                for (ix = minx; ix < maxx; ix++)
                {
                    bool targetIsInside = SourceIsIn[Helper.XYToIndex(ix, iy, sourceWidth)];// SourceIsIn[iy * sourceWidth + ix];
                    if (!targetIsInside)
                    {
                        continue;
                    }
                    dx = ix - sourceX;
                    distance = (int)Mathf.Sqrt(dx * dx + dy);
                    if (distance < min)
                    {
                        min = distance;
                    }
                }
            }

            if (-min < min_distance)
            {
                min_distance = -min;
            }

            TargetDistance[index] = -min;
        }

        MinDist[index] = min_distance;
        MaxDist[index] = max_distance;
    }
}

[BurstCompile]
public struct WriteTextureJob : IJobParallelFor
{
    [ReadOnly] public NativeArray<float> TargetDistance;
    [ReadOnly] public float minDistance;
    [ReadOnly] public float clampDist;

    public NativeArray<Color> TargetColors;

    public void Execute(int index)
    {
        var distance = TargetDistance[index] - minDistance;
        byte value = distance < 0f ? byte.MaxValue : (byte)(distance * byte.MaxValue / clampDist);
        TargetColors[index] = new Color32(value, value, value, byte.MaxValue);
    }
}